/*	--- 访问者 --- 


#	表示【一个作用于某对象结构】中的【各元素的操作】。

它使你可以在【不改变各元素的类】的【前提下】定义【作用】于【这些元素的新操作】。

==============================================================


【访问者模式】（Visitor）是一种【操作一组对象Object的操作】，它的目的是【不改变对象的定义】，但【允许新增不同的访问者】，来【定义新的操作】。

【访问者模式的设计】比较复杂，如果我们查看【GoF原始的访问者模式】，它是这么设计的：


   ┌─────────┐       ┌───────────────────────┐
   │ Client  │─ ─ ─ >│   Interface Visitor   │
   └─────────┘       ├───────────────────────┤
        │            │MethodA(ElementA)	     │
                     │MethodB(ElementB)      │
        │            └───────────────────────┘
                                 ▲  implements
    Main│                ┌───────┴───────┐
                         │               │
        │         ┌─────────────┐ ┌─────────────┐
                  │  VisitorA   │ │  VisitorB   │
        │         └─────────────┘ └─────────────┘
        ▼
┌───────────────┐        ┌───────────────┐
│ObjectStructure│─ ─ ─ ─>│    Element    │
├───────────────┤        ├───────────────┤
│handle(Visitor)│        │accept(Visitor)│ // 接收【访问者】
└───────────────┘        └───────────────┘
                                 ▲ extends
                        ┌────────┴────────┐
                        │                 │
                ┌───────────────┐ ┌───────────────┐
                │   Element_A   │ │   Element_B   │
                ├───────────────┤ ├───────────────┤
                │accept(Visitor)│ │accept(Visitor)│
                │doA()          │ │doB()          │
                └───────────────┘ └───────────────┘


上述模式的复杂之处在于: 上述【访问者模式】为了实现【所谓的“双重分派”】，设计了一个【回调再回调的机制】。

因为Java只支持【基于多态的单分派模式】，这里【强行模拟出“双重分派”】反而【加大了代码的复杂性】。

这里我们只介绍【简化的访问者模式】。

假设我们要【递归遍历】某个【文件夹的所有子文件夹和文件】，然后【找出.java文件】，正常的做法是【写个递归】： */
public class Scan_File {
	public static void main(String[] args) {
		
		void scan(File dir, List<File> collector) {
			for (File file : dir.listFiles()) { // scan File
				if (file.isFile() && file.getName().endsWith(".java")) {
					collector.add(file); // deal file
				} else if (file.isDir()) {
					// 递归调用
					scan(file, collector);
				}
			}
		}
	}
}


/*
上述代码的问题在于，【扫描目录的逻辑】和【处理.java文件】的逻辑混在了一起。

如果下次需要【增加一个清理.class文件】的功能，就【必须】再【重写扫描逻辑】。


因此，【访问者模式】先把【数据结构】（这里是【文件夹+文件】构成的【树型结构】）和【对其的操作】（查找文件）进行【分开】，

以后如果要【新增操作】（例如清理.class文件），只需要【新增访问者】，【不需要】改变【现有逻辑】。

---------------------

用【访问者模式】改写【上述代码】步骤如下：

首先，我们需要定义【访问者接口】，即【该访问者】能够干的事情： */
public interface Visitor {
	// 访问文件夹
	void visitDir(File dir);

	// 访问文件
	void visitFile(File file);
}


/*
紧接着，我们要定义【能持有文件夹和文件】的【数据结构FileStructure】： */
public class FileStructure {
	// 根目录
	private File path;

	public FileStructure(File path) {
		this.path = path;
	}
}

/*
然后，我们给【FileStructure】增加【一个handle()方法】，传入【一个访问者】： */
public class FileStructure {
	//...

	public void handle(Visitor visitor) {
		scan(this.path, visitor);
	}

	private void scan(File file, Visitor visitor) {
		if (file.isDirectory()) {
			// 让【访问者】处理【文件夹】
			visitor.visitDir(file);

			for (File sub : file.listFiles()) {
				// 递归处理【子文件夹】
				scan(sub, visitor); // Recursion
			}
		} else if (file.isFile()) {
			// 让【访问者】处理【文件】
			visitor.visitFile(file); 
		}
	}
}


/*
这样，我们就把【访问者的行为】抽象出来了。

如果我们要【实现一种操作】，例如，查找【.java文件】，就【传入JavaFileVisitor】： */
FileStructure fs = new FileStructure(new File("."));
fs.handle(new JavaFileVistor());


/*
这个【JavaFileVisitor实现】如下： */
public class JavaFileVisitor implements Visitor {

	public void visitDir(File dir) {
		System.out.println("Visit dir" + dir);
	}

	public void visitFile(File file) {
		if (file.getName().endsWith(".java")) {
			System.out.println("Found java file: " + file);
		}
	}
}


/*
类似的，如果要【清理.class文件】，可以再写一个【ClassFileClearnerVisitor】： */
public class ClassFileCleanerVisitor implements Visitor {
	public void visitDir(File dir) {
		//...
	}

	public void visitFile(File file) {
		if (file.getName().endsWith(".class")) {
			System.out.println("Will clean class file: " + file);
		}
	}
}


/*-------------

可见，【访问者模式】的核心思想是: 为了访问【比较复杂的数据结构】，【不去改变数据结构】，而是把【对数据的操作】抽象出来，使用【implements Class 调用另一个 implements Class的方法 处理 本Class】

【在“访问”的过程中】以【回调形式】在【访问者】中【处理操作逻辑】。

如果要新增【一组操作】，那么只需要【增加一个新的访问者】。

------------

实际上，【Java标准库】提供的
	Files.walkFileTree()方法
	
已经实现了【一个访问者模式】：*/
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class Main {
	public static void main(String[] args) throws IOException {
		Files.walkFileTree(Paths.get("."), new MyFileVisitor());
	}
}

// 实现一个FileVisitor:
class MyFileVisitor extends SimpleFileVisitor<Path> {
	// 处理Directory:
	public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
		System.out.println("pre visit dir: " + dir);
		// 返回CONTINUE 表示继续访问
		return FileVisitResult.CONTINUE;
	}

	// 处理File
	public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
		System.out.println("visit file: " + file);

		// 返回CONTINUE表示继续访问:
		return FileVisitResult.CONTINUE;
	}
}



/*
【Files.walkFileTree()】允许【访问者】返回【FileVisitResult.CONTINUE】以便【继续访问】，或者【返回FileVisitResult.TERMINATE】停止访问。

类似的，对【XML】的【SAX处理】也是一个【访问者模式】，我们需要提供一个【SAX Handler】作为【访问者】处理【XML的各个节点】。






/*==================================================


#	----- 访问者 の 小结 ----- 

1. 把【对数据的操作】抽象出来，使用【implements Class 调用另一个 implements Class的方法 处理 本Class】


-----------------------

总结精髓:

把【对数据的操作】抽象出来，使用【保存数据结构的Class】,在【数据class】中设置【另一个操作的Class】, 通过【设置不同的Class】来调用【统一的方法】从而实现【访问者模式】
(方法名全部统一,也就是使用了接口化树)

访问者模式 = 数据Class 访问 操作Class,Class级的多态


=================

总结模板: */

// Structure
public class FileStructure {
	// 根目录
	private File path;

	public FileStructure(File path) {
		this.path = path;
	}

	public void handle(Visitor visitor) {
		scan(this.path, visitor);
	}

	private void scan(File file, Visitor visitor) { // 传入【访问者】
		if (file.isDirectory()) { 
			visitor.visitDir(file); // 让【访问者】处理【文件夹】

			for (File sub : file.listFiles()) {
				// 递归处理【子文件夹】
				scan(sub, visitor); // Recursion
			}
		} else if (file.isFile()) {
			// 让【访问者】处理【文件】
			visitor.visitFile(file); 
		}
	}
}

// Visitor
public interface Visitor {
	// 访问文件夹
	void visitDir(File dir);

	// 访问文件
	void visitFile(File file);
}

public class JavaFileVisitor implements Visitor {

	public void visitDir(File dir) {
		System.out.println("Visit dir" + dir);
	}

	public void visitFile(File file) {
		if (file.getName().endsWith(".java")) {
			System.out.println("Found java file: " + file);
		}
	}
}

public class ClassFileCleanerVisitor implements Visitor {
	public void visitDir(File dir) {
		//...
	}

	public void visitFile(File file) {
		if (file.getName().endsWith(".class")) {
			System.out.println("Will clean class file: " + file);
		}
	}
}


// Main
FileStructure fs = new FileStructure(new File("."));
fs.handle(new JavaFileVistor());

























